package xutils

import (
	"encoding/binary"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"unsafe"
)

const (
	TypeId_User = iota
	TypeId_Byte
	TypeId_Int8
	TypeId_Int16
	TypeId_Int32
	TypeId_Int64
	TypeId_Int
	TypeId_Uint8
	TypeId_Uint16
	TypeId_Uint32
	TypeId_Uint64
	TypeId_Uint
	TypeId_String
	TypeId_Bytes
	TypeId_Bool
)

func Abs(v int) int {
	if v < 0 {
		return -v
	}

	return v
}

func AtoiNe(a string) int {
	if i, err := strconv.Atoi(a); err != nil {
		panic(err)
	} else {
		return i
	}
}

/*
 * 把一个 0 - F 的十六进制字符转为 0 - 15 的十进制值。
 */
func HtonNe(c byte) byte {
	if c >= '0' && c <= '9' {
		return byte(c - '0')
	} else if c >= 'a' && c <= 'f' {
		return byte(10 + c - 'a')
	} else if c >= 'A' && c <= 'F' {
		return byte(10 + c - 'A')
	}
	panic("Invalid heximal character.")
}

func Min(a, b int) int {
	if a >= b {
		return b
	}
	return a
}

func Max(a, b int) int {
	if a >= b {
		return a
	}
	return b
}

/**
 * 将一个字节串转换为 Ascii 字符串，不能显示的字符用 \xx 表示。
 */
//func Bytes2Ascii(data []byte) string {
//	buf := bytes.NewBuffer(make([]byte, 0, len(data)*4))
//	for i := 0; i < len(data); i++ {
//		b := data[i]
//		if b >= 32 && b <= 126 {
//			buf.PutByte(b)
//		} else {
//			s := fmt.Sprintf("\\%02x", b)
//			buf.Write([]byte(s))
//		}
//	}
//
//	return string(buf.Bytes())
//}

/**
 * 将字节数组强制转换为字符串。
 */
func Bytes2String(data []byte) string {
	return *(*string)(unsafe.Pointer(&data))
}

func CopyBytes(src []byte) []byte {
	dst := make([]byte, len(src))
	copy(dst, src)

	return dst
}

func Int2Bytes(data *int) []byte {
	slhead := reflect.SliceHeader{
		Data: uintptr(unsafe.Pointer(data)),
		Len:  int(unsafe.Sizeof(int(1))),
		Cap:  int(unsafe.Sizeof(int(1))),
	}
	return *(*[]byte)(unsafe.Pointer(&slhead))
}

/**
 * 将字符串强制转换为字节数组。
 */
func String2Bytes(data string) []byte {
	slhead := reflect.SliceHeader{}
	strhead := *(*reflect.StringHeader)(unsafe.Pointer(&data))
	slhead.Data = strhead.Data
	slhead.Len = strhead.Len
	slhead.Cap = strhead.Len
	return *(*[]byte)(unsafe.Pointer(&slhead))
}

func String2Addr(addr string) (*net.TCPAddr, error) {
	return net.ResolveTCPAddr("tcp4", addr)
}

func String2AddrNe(addr string) *net.TCPAddr {
	a, err := net.ResolveTCPAddr("tcp4", addr)
	if err != nil {
		panic(err)
	}

	return a
}

// 返回函数调用栈中向上 skip 层的函数名称
// skip == 0 时，应当返回 "xutils.Caller"
func Caller(skip int) string {
	pc, _, _, ok := runtime.Caller(skip)
	if !ok {
		return ""
	}

	fn := runtime.FuncForPC(pc).Name()
	i := strings.LastIndexByte(fn, '/')

	return fn[i+1:]
}

func FileLine(skip int) (string, int) {
	_, file, line, _ := runtime.Caller(skip)
	i := strings.LastIndexByte(file, '/')

	return file[i+1:], line
}

func FileExists(filename string) bool {
	_, err := os.Stat(filename)
	// err 不为 nil 时，应该使用 os.IsNotExist 来判断是否确定是文件不存在
	// 这里不考虑了，只要错误就是文件无法访问
	return err == nil
}

// 返回本程序可执行文件的绝对路径。
func ProgramPath() string {
	f, err := exec.LookPath(os.Args[0])
	if err != nil {
		return ""
	}
	p, err := filepath.Abs(f)
	if err != nil {
		return ""
	}
	return p
}

// 获取本程序文件所在的绝对目录。返回字符中保留最后的 "/"。
func ProgramDir() string {
	p := ProgramPath()
	i := strings.LastIndex(p, "/")
	if i < 0 {
		return ""
	}
	return p[:i+1]
}

func IsAlpha(c byte) bool {
	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}

func IsDigit(c byte) bool {
	return c >= '0' && c <= '9'
}

func IsAlphaDigit(c byte) bool {
	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
}

func IsHeximal(c byte) bool {
	return (c >= '0' && c <= '0') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')
}

func IsSpace(c byte) bool {
	return c == ' ' || c == '\t'
}

func IsWhite(c byte) bool {
	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}

func IsPrintable(c byte) bool {
	return c >= 32 && c <= 126
}

/**
 * 将一个长字符串改变为一个多行字符串。
 *
 * str          - 要处理的字符串
 * line_length  - 处理后字符串的每行长度（不包括前缀）
 * first_prefix - 首行前缀
 * other_prefix - 其它行前缀
 */
/*
func StrMultiline(str string, line_length int, first_prefix string, other_prefix string) string {
	str_parts := make([]string, 0, 32)

	if len(str) <= line_length {
		str_parts = append(str_parts, first_prefix, str)
	} else {
		str_parts = append(str_parts, first_prefix, str[:line_length], "\n")
		str = str[line_length:]
		for len(str) > line_length {
			str_parts = append(str_parts, other_prefix, str[:line_length], "\n")
			str = str[line_length:]
		}
		str_parts = append(str_parts, other_prefix, str)
	}

	return strings.Join(str_parts, "")
}
*/

func ReadUint32(r io.Reader) (uint32, error) {
	var buf [4]byte

	b := buf[:]
	if _, err := io.ReadFull(r, b); err != nil {
		return 0, err
	}

	return binary.BigEndian.Uint32(b), nil
}

func WriteUint32(w io.Writer, data uint32) error {
	var buf [4]byte

	b := buf[:]
	binary.BigEndian.PutUint32(b, data)

	if err := WriteFull(w, b); err != nil {
		return err
	}

	return nil
}

func WriteFull(w io.Writer, data []byte) error {
	dataLen := len(data)
	nWritten := 0

	for nWritten < dataLen {
		if n, err := w.Write(data[nWritten:]); err != nil {
			return err
		} else {
			nWritten += n
		}
	}

	return nil
}
